package com.matt.service.impl;

import com.matt.domain.LiveRoom;
import com.matt.domain.RelClassesTeachingArea;
import com.matt.domain.TeachingArea;
import com.matt.foundation.constant.LiveRoomConstant;
import com.matt.commons.exception.BusinessException;

import com.matt.foundation.model.ErrorCode;
import com.matt.commons.page.PagerQuery;
import com.matt.foundation.utils.annotation.OptsTrace;
import com.matt.foundation.vo.TeachingAreaPageVo;
import com.matt.foundation.vo.TeachingAreaVo;
import com.matt.repository.LiveRoomRepository;
import com.matt.repository.TeachingAreaRepository;
import com.matt.repository.rel.RelClassesTeachingAreaRepository;
import com.matt.service.TeachingAreaService;
import org.apache.commons.lang.StringUtils;
import org.assertj.core.util.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;
import java.util.*;
import java.util.stream.Collectors;

import static com.matt.foundation.model.ErrorCode.AREA_TEACHING_AREA_DUPLICATE;

/**
 * *******************************
 * Created by Ron.
 * form com.matt
 * on 2017/9/4
 * *******************************
 */
@Service
public class TeachingAreaServiceImpl implements TeachingAreaService{

    @Autowired
    private TeachingAreaRepository teachingAreaRepository;

    @Autowired
    private LiveRoomRepository liveRoomRepository;

    @Autowired
    private RelClassesTeachingAreaRepository relClassesTeachingAreaRepository;

    @Override
    public List<TeachingAreaPageVo> getTeachingAreaByArea(String areaId) {
        List<TeachingArea> teachingAreaList = teachingAreaRepository.findByAreaIdAndDeleted(areaId, false);
        List<TeachingAreaPageVo> voList = Lists.newArrayList();
        for (TeachingArea teachingArea : teachingAreaList){
            List<LiveRoom> liveRoomList = liveRoomRepository.findByTeachingAreaIdAndStatus(teachingArea.getId(), LiveRoomConstant.Status.LIVING.codeOf());
            voList.add(TeachingAreaPageVo.of(teachingArea, liveRoomList));
        }
        return voList;
    }

    @Override
    public Page<TeachingAreaPageVo> pageTeachingAreaByArea(String areaId, Boolean publicFilter,  PagerQuery query) {

        Specification<TeachingArea> specification = new Specification<TeachingArea>() {
            @Override
            public Predicate toPredicate(Root<TeachingArea> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<>();

                if (StringUtils.isNotBlank(areaId))
                    predicates.add(criteriaBuilder.and(criteriaBuilder.equal(root.get("areaId"), areaId)));

                if(publicFilter != null){
                    predicates.add(criteriaBuilder.and(criteriaBuilder.equal(root.get("isPublic"), publicFilter)));
                }

                query.where(predicates.toArray(new Predicate[predicates.size()]));
                query.orderBy(criteriaBuilder.desc(root.get("created")));
                return query.getRestriction();
            }
        };
        Pageable pageable = PageRequest.of(query.getPageNum(), query.getPageSize());

        Page<TeachingArea> teachingAreaPage = teachingAreaRepository.findAll(specification, pageable);

        List<String> teachingAreaIds = teachingAreaPage.getContent().stream().map(k->k.getId()).collect(Collectors.toList());
        List<LiveRoom> liveRoomList = liveRoomRepository.findByTeachingAreaIdInAndStatus(teachingAreaIds, LiveRoomConstant.Status.LIVING.codeOf());
        Map<String, List<LiveRoom>> cache = new HashMap<>();
        for(LiveRoom liveRoom : liveRoomList){
            List<LiveRoom> liveRooms = cache.get(liveRoom.getTeachingAreaId());
            if(liveRooms == null){
                liveRooms = new ArrayList<>();
                cache.put(liveRoom.getTeachingAreaId(), liveRooms);
            }
            liveRooms.add(liveRoom);
        }

        List<TeachingAreaPageVo> voList = Lists.newArrayList();
        for (TeachingArea teachingArea : teachingAreaPage.getContent()){
            voList.add(TeachingAreaPageVo.of(teachingArea, cache.get(teachingArea.getId())));
        }

        return new PageImpl<TeachingAreaPageVo>(voList, PageRequest.of(query.getPageNum(), query.getPageSize()), teachingAreaPage.getTotalElements());
    }

    @Transactional
    @OptsTrace(code = "0601")
    @Override
    public TeachingArea addTeachingArea(String name, String remark, String[] cameraIds, String areaId, Boolean isPublic) {
        TeachingArea area = teachingAreaRepository.findByNameAndAreaId(name, areaId);
        if (Objects.nonNull(area))
            throw new BusinessException(AREA_TEACHING_AREA_DUPLICATE, "教学区域名称重复");

        TeachingArea teachingArea = new TeachingArea();
        teachingArea.setAreaId(areaId);
        teachingArea.setName(name);
        teachingArea.setRemark(remark);
        teachingArea.setIsPublic(isPublic==null?false:isPublic);

        teachingAreaRepository.save(teachingArea);
        if(null != cameraIds && cameraIds.length !=0){
            List<LiveRoom> liveRooms = liveRoomRepository.findByIdInAndStatus(cameraIds, LiveRoomConstant.Status.LIVING.codeOf());
            liveRooms.forEach(o->o.setTeachingAreaId(teachingArea.getId()));
            liveRoomRepository.saveAll(liveRooms);
            teachingArea.setCameraSize(liveRooms.size());
        }else{
            teachingArea.setCameraSize(0);
        }

        teachingAreaRepository.save(teachingArea);
        return teachingArea;
    }

    @Override
    public TeachingAreaVo getTeachingAreaById(String teachingAreaId, String areaId) {
        TeachingArea teachingArea = teachingAreaRepository.findByAreaIdAndId(areaId, teachingAreaId);
        if (teachingArea == null){
            throw new BusinessException(ErrorCode.AREA_TEACHING_AREA_NOT_EXIST, "教学区域不存在");
        }
        List<LiveRoom> roomList = liveRoomRepository.findByTeachingAreaIdAndStatus(teachingAreaId, LiveRoomConstant.Status.LIVING.codeOf());
        return TeachingAreaVo.of(teachingArea, roomList);
    }

    @Override
    public TeachingAreaVo getTeachingAreaById(String teachingAreaId) {
        TeachingArea teachingArea = teachingAreaRepository.getOne(teachingAreaId);
        if (teachingArea == null){
            throw new BusinessException(ErrorCode.AREA_TEACHING_AREA_NOT_EXIST, "教学区域不存在");
        }
        List<LiveRoom> roomList = liveRoomRepository.findByTeachingAreaIdAndStatus(teachingAreaId, LiveRoomConstant.Status.LIVING.codeOf());
        return TeachingAreaVo.of(teachingArea, roomList);
    }

    @Transactional
    @OptsTrace(code = "0602")
    @Override
    public TeachingArea modify(String teachingAreaId, String name, String remark, String[] cameraIds, String areaId, Boolean isPublic) {
        TeachingArea area = teachingAreaRepository.findByNameAndAreaId(name, areaId);
        if (Objects.nonNull(area) && !area.getId().equals(teachingAreaId))
            throw new BusinessException(AREA_TEACHING_AREA_DUPLICATE, "教学区域名称重复");

        TeachingArea teachingArea = teachingAreaRepository.getOne(teachingAreaId);
        teachingArea.setName(name);
        teachingArea.setRemark(remark);
        teachingArea.setIsPublic(isPublic==null?false:isPublic);
        if (Objects.nonNull(cameraIds)){
            teachingArea.setCameraSize(cameraIds.length);
        }
        if (teachingArea.getIsPublic()){
            List<RelClassesTeachingArea>  relClassesTeachingAreas = relClassesTeachingAreaRepository.findByTeachingAreaId(teachingAreaId);
            if (relClassesTeachingAreas.size() >0 ){
                throw new BusinessException(AREA_TEACHING_AREA_DUPLICATE, "教学区域已使用，请勿修改！");
            }
        }

        List<LiveRoom> roomList = liveRoomRepository.findByTeachingAreaIdAndStatus(teachingAreaId, LiveRoomConstant.Status.LIVING.codeOf());
        teachingAreaRepository.save(teachingArea);

        roomList.forEach(o-> {
            o.setTeachingAreaId(null);
            liveRoomRepository.save(o);
        });
        if (Objects.nonNull(cameraIds)){
            List<LiveRoom> liveRooms = liveRoomRepository.findByIdInAndStatus(cameraIds, LiveRoomConstant.Status.LIVING.codeOf());
            liveRooms.forEach(o->o.setTeachingAreaId(teachingArea.getId()));
            liveRoomRepository.saveAll(liveRooms);
        }
        return teachingArea;
    }
}
